home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GameStar 2004 April
/
Gamestar_61_2004-04_dvdb.iso
/
DVDStar
/
Editace
/
hltp.exe
/
{app}
/
Source Code
/
Zoners Half-Life Tools
/
hlcsg
/
map.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2002-11-28
|
21KB
|
626 lines
#include "csg.h"
int g_nummapbrushes;
brush_t g_mapbrushes[MAX_MAP_BRUSHES];
int g_numbrushsides;
side_t g_brushsides[MAX_MAP_SIDES];
int g_nMapFileVersion;
static const vec3_t s_baseaxis[18] = {
{0, 0, 1}, {1, 0, 0}, {0, -1, 0}, // floor
{0, 0, -1}, {1, 0, 0}, {0, -1, 0}, // ceiling
{1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // west wall
{-1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // east wall
{0, 1, 0}, {1, 0, 0}, {0, 0, -1}, // south wall
{0, -1, 0}, {1, 0, 0}, {0, 0, -1}, // north wall
};
// =====================================================================================
// TextureAxisFromPlane
// =====================================================================================
void TextureAxisFromPlane(const plane_t* const pln, vec3_t xv, vec3_t yv)
{
int bestaxis;
vec_t dot, best;
int i;
best = 0;
bestaxis = 0;
for (i = 0; i < 6; i++)
{
dot = DotProduct(pln->normal, s_baseaxis[i * 3]);
if (dot > best)
{
best = dot;
bestaxis = i;
}
}
VectorCopy(s_baseaxis[bestaxis * 3 + 1], xv);
VectorCopy(s_baseaxis[bestaxis * 3 + 2], yv);
}
#define ScaleCorrection (1.0/128.0)
// =====================================================================================
// CopySKYtoCLIP
// clips a particluar sky brush
// =====================================================================================
static void CopySKYtoCLIP(const brush_t* const b)
{
int i;
entity_t* mapent;
brush_t* newbrush;
if (b->contents != CONTENTS_SKY)
Error("[MOD] CopySKYtoCLIP: Got a NON-SKY for passed brush! (%s)",b->contents );
hlassert(b->contents == CONTENTS_SKY); // Only SKY brushes should be passed down to this function(sanity check)
hlassert(b->entitynum == 0); // SKY must be in worldspawn entity
mapent = &g_entities[b->entitynum];
mapent->numbrushes++;
newbrush = &g_mapbrushes[g_nummapbrushes];
newbrush->entitynum = b->entitynum;
newbrush->brushnum = g_nummapbrushes - mapent->firstbrush;
newbrush->firstside = g_numbrushsides;
newbrush->numsides = b->numsides;
newbrush->contents = CONTENTS_CLIP;
newbrush->noclip = 0;
for (i = 0; i < b->numsides; i++)
{
int j;
side_t* side = &g_brushsides[g_numbrushsides];
*side = g_brushsides[b->firstside + i];
safe_strncpy(side->td.name, "CLIP", sizeof(side->td.name));
for (j = 0; j < NUM_HULLS; j++)
{
newbrush->hulls[j].faces = NULL;
newbrush->hulls[j].bounds = b->hulls[j].bounds;
}
g_numbrushsides++;
hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
}
g_nummapbrushes++;
hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
}
// =====================================================================================
// HandleSKYCLIP
// clips the whole sky, unconditional of g_skyclip
// =====================================================================================
static void HandleSKYCLIP()
{
int i;
int last;
entity_t* e = &g_entities[0];
for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++)
{
if (g_mapbrushes[i].contents == CONTENTS_SKY)
{
CopySKYtoCLIP(&g_mapbrushes[i]);
}
}
}
// =====================================================================================
// ParseBrush
// parse a brush from script
// =====================================================================================
static contents_t ParseBrush(entity_t* mapent)
{
brush_t* b;
int i, j;
side_t* side;
contents_t contents;
bool ok;
hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
b = &g_mapbrushes[g_nummapbrushes];
g_nummapbrushes++;
b->firstside = g_numbrushsides;
b->entitynum = g_numentities - 1;
b->brushnum = g_nummapbrushes - mapent->firstbrush - 1;
#ifdef HLCSG_CLIPECONOMY // AJM
b->noclip = 0;
#endif
mapent->numbrushes++;
ok = GetToken(true);
while (ok)
{
g_TXcommand = 0;
if (!strcmp(g_token, "}"))
{
break;
}
hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
side = &g_brushsides[g_numbrushsides];
g_numbrushsides++;
b->numsides++;
// read the three point plane definition
for (i = 0; i < 3; i++)
{
if (i != 0)
{
GetToken(true);
}
if (strcmp(g_token, "("))
{
Error("Parsing Entity %i, Brush %i, Side %i : Expecting '(' got '%s'",
b->entitynum, b->brushnum, b->numsides, g_token);
}
for (j = 0; j < 3; j++)
{
GetToken(false);
side->planepts[i][j] = atof(g_token);
}
GetToken(false);
if (strcmp(g_token, ")"))
{
Error("Parsing Entity %i, Brush %i, Side %i : Expecting ')' got '%s'",
b->entitynum, b->brushnum, b->numsides, g_token);
}
}
// read the texturedef
GetToken(false);
strupr(g_token);
safe_strncpy(side->td.name, g_token, sizeof(side->td.name));
if (g_nMapFileVersion < 220) // Worldcraft 2.1-, Radiant
{
GetToken(false);
side->td.vects.valve.shift[0] = atof(g_token);
GetToken(false);
side->td.vects.valve.shift[1] = atof(g_token);
GetToken(false);
side->td.vects.valve.rotate = atof(g_token);
GetToken(false);
side->td.vects.valve.scale[0] = atof(g_token);
GetToken(false);
side->td.vects.valve.scale[1] = atof(g_token);
}
else // Worldcraft 2.2+
{
// texture U axis
GetToken(false);
if (strcmp(g_token, "["))
{
hlassume(false, assume_MISSING_BRACKET_IN_TEXTUREDEF);
}
GetToken(false);
side->td.vects.valve.UAxis[0] = atof(g_token);
GetToken(false);
side->td.vects.valve.UAxis[1] = atof(g_token);
GetToken(false);
side->td.vects.valve.UAxis[2] = atof(g_token);
GetToken(false);
side->td.vects.valve.shift[0] = atof(g_token);
GetToken(false);
if (strcmp(g_token, "]"))
{
Error("missing ']' in texturedef (U)");
}
// texture V axis
GetToken(false);
if (strcmp(g_token, "["))
{
Error("missing '[' in texturedef (V)");
}
GetToken(false);
side->td.vects.valve.VAxis[0] = atof(g_token);
GetToken(false);
side->td.vects.valve.VAxis[1] = atof(g_token);
GetToken(false);
side->td.vects.valve.VAxis[2] = atof(g_token);
GetToken(false);
side->td.vects.valve.shift[1] = atof(g_token);
GetToken(false);
if (strcmp(g_token, "]"))
{
Error("missing ']' in texturedef (V)");
}
// Texture rotation is implicit in U/V axes.
GetToken(false);
side->td.vects.valve.rotate = 0;
// texure scale
GetToken(false);
side->td.vects.valve.scale[0] = atof(g_token);
GetToken(false);
side->td.vects.valve.scale[1] = atof(g_token);
}
ok = GetToken(true); // Done with line, this reads the first item from the next line
if ((g_TXcommand == '1' || g_TXcommand == '2'))
{
// We are QuArK mode and need to translate some numbers to align textures its way
// from QuArK, the texture vectors are given directly from the three points
vec3_t TexPt[2];
int k;
float dot22, dot23, dot33, mdet, aa, bb, dd;
k = g_TXcommand - '0';
for (j = 0; j < 3; j++)
{
TexPt[1][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection;
}
k = 3 - k;
for (j = 0; j < 3; j++)
{
TexPt[0][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection;
}
dot22 = DotProduct(TexPt[0], TexPt[0]);
dot23 = DotProduct(TexPt[0], TexPt[1]);
dot33 = DotProduct(TexPt[1], TexPt[1]);
mdet = dot22 * dot33 - dot23 * dot23;
if (mdet < 1E-6 && mdet > -1E-6)
{
aa = bb = dd = 0;
Warning
("Degenerate QuArK-style brush texture : Entity %i, Brush %i @ (%f,%f,%f) (%f,%f,%f) (%f,%f,%f)",
b->entitynum, b->brushnum, side->planepts[0][0], side->planepts[0][1], side->planepts[0][2],
side->planepts[1][0], side->planepts[1][1], side->planepts[1][2], side->planepts[2][0],
side->planepts[2][1], side->planepts[2][2]);
}
else
{
mdet = 1.0 / mdet;
aa = dot33 * mdet;
bb = -dot23 * mdet;
//cc = -dot23*mdet; // cc = bb
dd = dot22 * mdet;
}
for (j = 0; j < 3; j++)
{
side->td.vects.quark.vects[0][j] = aa * TexPt[0][j] + bb * TexPt[1][j];
side->td.vects.quark.vects[1][j] = -( /*cc */ bb * TexPt[0][j] + dd * TexPt[1][j]);
}
side->td.vects.quark.vects[0][3] = -DotProduct(side->td.vects.quark.vects[0], side->planepts[0]);
side->td.vects.quark.vects[1][3] = -DotProduct(side->td.vects.quark.vects[1], side->planepts[0]);
}
side->td.txcommand = g_TXcommand; // Quark stuff, but needs setting always
};
b->contents = contents = CheckBrushContents(b);
//
// origin brushes are removed, but they set
// the rotation origin for the rest of the brushes
// in the entity
//
if (contents == CONTENTS_ORIGIN)
{
char string[MAXTOKEN];
vec3_t origin;
b->contents = CONTENTS_SOLID;
CreateBrush(mapent->firstbrush + b->brushnum); // to get sizes
b->contents = contents;
for (i = 0; i < NUM_HULLS; i++)
{
b->hulls[i].faces = NULL;
}
if (b->entitynum != 0) // Ignore for WORLD (code elsewhere enforces no ORIGIN in world message)
{
VectorAdd(b->hulls[0].bounds.m_Mins, b->hulls[0].bounds.m_Maxs, origin);
VectorScale(origin, 0.5, origin);
safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(&g_entities[b->entitynum], "origin", string);
}
}
return contents;
}
// =====================================================================================
// ParseMapEntity
// parse an entity from script
// =====================================================================================
bool ParseMapEntity()
{
bool all_clip = true;
int this_entity;
entity_t* mapent;
epair_t* e;
if (!GetToken(true))
{
return false;
}
this_entity = g_numentities;
if (strcmp(g_token, "{"))
{
Error("Parsing Entity %i, expected '{' got '%s'", this_entity, g_token);
}
hlassume(g_numentities < MAX_MAP_ENTITIES, assume_MAX_MAP_ENTITIES);
g_numentities++;
mapent = &g_entities[this_entity];
mapent->firstbrush = g_nummapbrushes;
mapent->numbrushes = 0;
while (1)
{
if (!GetToken(true))
Error("ParseEntity: EOF without closing brace");
if (!strcmp(g_token, "}")) // end of our context
break;
if (!strcmp(g_token, "{")) // must be a brush
{
contents_t contents = ParseBrush(mapent);
if ((contents != CONTENTS_CLIP) && (contents != CONTENTS_ORIGIN))
all_clip = false;
}
else // else assume an epair
{
e = ParseEpair();
if (!strcmp(e->key, "mapversion"))
{
g_nMapFileVersion = atoi(e->value);
}
e->next = mapent->epairs;
mapent->epairs = e;
}
}
if (mapent->numbrushes && all_clip)
Fatal(assume_NO_VISIBILE_BRUSHES, "Entity %i has no visible brushes\n", this_entity);
CheckFatal();
#ifdef ZHLT_DETAIL // AJM
if (!strcmp(ValueForKey(mapent, "classname"), "info_detail") && g_bDetailBrushes && this_entity != 0)
{
// mark all of the brushes in this entity as contents_detail
for (int i = mapent->firstbrush; i < mapent->firstbrush + mapent->numbrushes; i++)
{
g_mapbrushes[i].contents = CONTENTS_DETAIL;
}
// move these brushes to worldspawn
{
brush_t* temp;
int newbrushes;
int worldbrushes;
int i;
newbrushes = mapent->numbrushes;
worldbrushes = g_entities[0].numbrushes;
temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t));
memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t));
for (i = 0; i < newbrushes; i++)
{
temp[i].entitynum = 0;
}
// make space to move the brushes (overlapped copy)
memmove(g_mapbrushes + worldbrushes + newbrushes,
g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes));
// copy the new brushes down
memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes);
// fix up indexes
g_numentities--;
g_entities[0].numbrushes += newbrushes;
for (i = 1; i < g_numentities; i++)
{
g_entities[i].firstbrush += newbrushes;
}
memset(mapent, 0, sizeof(*mapent));
Free(temp);
}
// delete this entity
g_numentities--;
return true;
}
#endif
#ifdef ZHLT_INFO_COMPILE_PARAMETERS // AJM
if (!strcmp(ValueForKey(mapent, "classname"), "info_compile_parameters"))
{
GetParamsFromEnt(mapent);
}
#endif
// if its the worldspawn entity and we need to skyclip, then do it
if ((this_entity == 0) && g_skyclip) // first entitiy
{
HandleSKYCLIP();
}
// if the given entity only has one brush and its an origin brush
if ((mapent->numbrushes == 1) && (g_mapbrushes[mapent->firstbrush].contents == CONTENTS_ORIGIN))
{
brushhull_t* hull = g_mapbrushes[mapent->firstbrush].hulls;
Error("Entity %i, contains ONLY an origin brush near (%.0f,%.0f,%.0f)\n",
this_entity, hull->bounds.m_Mins[0], hull->bounds.m_Mins[1], hull->bounds.m_Mins[2]);
}
GetVectorForKey(mapent, "origin", mapent->origin);
// group entities are just for editor convenience
// toss all brushes into the world entity
if (!g_onlyents && !strcmp("func_group", ValueForKey(mapent, "classname")))
{
// this is pretty gross, because the brushes are expected to be
// in linear order for each entity
brush_t* temp;
int newbrushes;
int worldbrushes;
int i;
newbrushes = mapent->numbrushes;
worldbrushes = g_entities[0].numbrushes;
temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t));
memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t));
for (i = 0; i < newbrushes; i++)
{
temp[i].entitynum = 0;
}
// make space to move the brushes (overlapped copy)
memmove(g_mapbrushes + worldbrushes + newbrushes,
g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes));
// copy the new brushes down
memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes);
// fix up indexes
g_numentities--;
g_entities[0].numbrushes += newbrushes;
for (i = 1; i < g_numentities; i++)
{
g_entities[i].firstbrush += newbrushes;
}
memset(mapent, 0, sizeof(*mapent));
Free(temp);
}
return true;
}
// =====================================================================================
// CountEngineEntities
// =====================================================================================
unsigned int CountEngineEntities()
{
unsigned int x;
unsigned num_engine_entities = 0;
entity_t* mapent = g_entities;
// for each entity in the map
for (x=0; x<g_numentities; x++, mapent++)
{
const char* classname = ValueForKey(mapent, "classname");
// if its a light_spot or light_env, dont include it as an engine entity!
if (classname)
{
if ( !strncasecmp(classname, "light", 5)
|| !strncasecmp(classname, "light_spot", 10)
|| !strncasecmp(classname, "light_environment", 17)
)
{
const char* style = ValueForKey(mapent, "style");
const char* targetname = ValueForKey(mapent, "targetname");
// lightspots and lightenviroments dont have a targetname or style
if (!strlen(targetname) && !atoi(style))
{
continue;
}
}
}
num_engine_entities++;
}
return num_engine_entities;
}
// =====================================================================================
// LoadMapFile
// wrapper for LoadScriptFile
// parse in script entities
// =====================================================================================
const char* ContentsToString(const contents_t type);
void LoadMapFile(const char* const filename)
{
unsigned num_engine_entities;
LoadScriptFile(filename);
g_numentities = 0;
while (ParseMapEntity())
{
}
// AJM debug
/*
for (int i = 0; i < g_numentities; i++)
{
Log("entity: %i - %i brushes - %s\n", i, g_entities[i].numbrushes, ValueForKey(&g_entities[i], "classname"));
}
Log("total entities: %i\ntotal brushes: %i\n\n", g_numentities, g_nummapbrushes);
for (i = g_entities[0].firstbrush; i < g_entities[0].firstbrush + g_entities[0].numbrushes; i++)
{
Log("worldspawn brush %i: contents %s\n", i, ContentsToString((contents_t)g_mapbrushes[i].contents));
}
*/
num_engine_entities = CountEngineEntities();
hlassume(num_engine_entities < MAX_ENGINE_ENTITIES, assume_MAX_ENGINE_ENTITIES);
CheckFatal();
Verbose("Load map:%s\n", filename);
Verbose("%5i brushes\n", g_nummapbrushes);
Verbose("%5i map entities \n", g_numentities - num_engine_entities);
Verbose("%5i engine entities\n", num_engine_entities);
// AJM: added in
#ifdef HLCSG_AUTOWAD
GetUsedTextures();
#endif
}